feat: ai-chat reference project + MCP agent-chat tooling (4/4)#3546
feat: ai-chat reference project + MCP agent-chat tooling (4/4)#3546ericallam wants to merge 4 commits into
Conversation
🦋 Changeset detectedLatest commit: fef74f4 The changes in this PR will be included in the next version bump. This PR includes changesets to release 32 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
| }, | ||
| }); | ||
| session.runId = result.id; | ||
| session.lastEventId = undefined; |
There was a problem hiding this comment.
🔴 Resetting lastEventId to undefined on upgrade causes SSE replay of entire session history
In collectAgentResponse, when a trigger:upgrade-required chunk is received, session.lastEventId is set to undefined (line 390) before recursively calling collectAgentResponse. The recursive call creates a new SSEStreamSubscription using session.lastEventId (now undefined) as the lastEventId option (agentChat.ts:338), which means the new subscription replays the entire session .out stream from the very beginning.
Since session .out is a durable stream containing all historical chunks across runs, the replayed events will include old turns' trigger:turn-complete chunks. The first historical trigger:turn-complete hit (line 366-368) immediately breaks the collection loop, causing the function to return with empty/partial text from a previous turn instead of the new run's response.
Expected fix
Keep session.lastEventId as-is (pointing to the trigger:upgrade-required chunk's SSE id) so the recursive subscription resumes right after the upgrade marker, where the new run's output will appear.
Was this helpful? React with 👍 or 👎 to provide feedback.
| packageVersion: buildManifest.packageVersion, | ||
| cliPackageVersion: buildManifest.cliPackageVersion, | ||
| tasks: workerManifest.tasks, | ||
| prompts: workerManifest.prompts, |
There was a problem hiding this comment.
🚩 managed-index-controller adds prompts but not skills to worker registration payload
Both dev-index-worker.ts:187 and managed-index-worker.ts:183 now emit skills: resourceCatalog.listSkillManifests() in their worker manifests. The managed-index-controller.ts forwards tasks, prompts (newly added at line 107), and queues to the server — but does NOT forward skills. If skills need to be registered server-side (for dashboard display, deploy metadata, etc.), this omission would silently drop them in the managed (production) indexing path. Could be intentional if skills are only used at runtime and don't need server-side registration, but it's asymmetric with the prompts addition in the same PR.
Was this helpful? React with 👍 or 👎 to provide feedback.
ecfac76 to
7ee523e
Compare
882544c to
5baac29
Compare
7ee523e to
fdc61c6
Compare
5baac29 to
e16efbb
Compare
fdc61c6 to
96700b1
Compare
e16efbb to
f4a7923
Compare
96700b1 to
482d752
Compare
f4a7923 to
04b98af
Compare
482d752 to
920e876
Compare
04b98af to
417344a
Compare
920e876 to
d96e2f7
Compare
417344a to
43fde3f
Compare
d96e2f7 to
3256f42
Compare
43fde3f to
c27cef9
Compare
3256f42 to
220b33c
Compare
c27cef9 to
48f030a
Compare
220b33c to
067109f
Compare
48f030a to
3c71eeb
Compare
067109f to
c1f6db7
Compare
3c71eeb to
866b175
Compare
c1f6db7 to
f75bcd8
Compare
866b175 to
2d9bcdb
Compare
f75bcd8 to
40a3dff
Compare
2d9bcdb to
d9fa1be
Compare
40a3dff to
1748445
Compare
d9fa1be to
b7ed332
Compare
8d8a1a6 to
31b1eba
Compare
e908afb to
711f8dc
Compare
31b1eba to
f794838
Compare
711f8dc to
80155bf
Compare
f794838 to
f481bb4
Compare
80155bf to
165cfc1
Compare
f481bb4 to
dbdabcf
Compare
165cfc1 to
c2bc48f
Compare
dbdabcf to
fcace0c
Compare
c2bc48f to
78a26a6
Compare
fcace0c to
8615ad0
Compare
78a26a6 to
5e0526a
Compare
8615ad0 to
f7197ef
Compare
5e0526a to
36ccf22
Compare
36ccf22 to
a90949b
Compare
f7197ef to
5c937df
Compare
75369f3 to
cf2fa07
Compare
| prompts: workerManifest.prompts, | ||
| queues: workerManifest.queues, |
There was a problem hiding this comment.
🔴 Managed-index-controller omits skills from deployment registration body
Both dev-index-worker.ts:187 and managed-index-worker.ts:183 now include skills: resourceCatalog.listSkillManifests() in the worker manifest, and the managed-index-controller was updated to forward the new prompts field (prompts: workerManifest.prompts at line 107). However, the corresponding skills: workerManifest.skills line was not added to the backgroundWorkerBody.metadata object. This means skill manifests registered via skills.define() will be collected by the worker during managed (production) deployments but silently dropped when the controller sends the registration request to the server — skills won't be available server-side in deployed environments.
| prompts: workerManifest.prompts, | |
| queues: workerManifest.queues, | |
| prompts: workerManifest.prompts, | |
| skills: workerManifest.skills, |
Was this helpful? React with 👍 or 👎 to provide feedback.
| session.runId = result.id; | ||
| session.lastEventId = undefined; | ||
| reader.releaseLock(); | ||
| // Recurse — subscribe to the new run's stream (same session.out URL) | ||
| return collectAgentResponse(session); | ||
| } |
There was a problem hiding this comment.
🚩 collectAgentResponse recursive upgrade path clears lastEventId
On the trigger:upgrade-required path (line 407), session.lastEventId is set to undefined before recursing into collectAgentResponse. The same pattern appears in the sendAgentMessageTool error fallback (line 251). If the S2 session stream replays from the beginning when no Last-Event-ID header is sent, old trigger:turn-complete markers from prior turns would cause the collector to break immediately at line 378-379, returning empty text. Whether this is a real problem depends on the S2 stream's replay semantics — if the server only sends events from the current tail when no Last-Event-ID is provided, this is fine. If it replays the full stream, the MCP tool would return empty responses on the fallback/upgrade paths. The browser transport presumably handles this correctly, so the MCP tool should follow the same pattern. Worth verifying with the S2 implementation.
Was this helpful? React with 👍 or 👎 to provide feedback.
| @@ -0,0 +1 @@ | |||
| lib/generated/ | |||
There was a problem hiding this comment.
🚩 Generated Prisma files committed in references/ai-chat
The references/ai-chat/lib/generated/prisma/ directory contains Prisma-generated client files checked into git. The .gitignore at references/ai-chat/.gitignore lists lib/generated/ which should exclude these, but the files are present in the diff. This might indicate the gitignore was added after the files were committed, or the files were force-added. These files add significant noise to the PR diff (~5000+ lines of generated code). Worth verifying the gitignore is working and removing tracked generated files if appropriate.
Was this helpful? React with 👍 or 👎 to provide feedback.
Top of the chat.agent stack: a full Next.js reference project that exercises chat.agent end-to-end, plus the CLI MCP tools that drive agent runs from Claude Code / Cursor / etc. references/ai-chat: - Full Next.js app with prisma persistence, multi-chat sidebar, per-chat model picker, debug panel, tool examples, smoke tests - Reference tools: getCurrentTime, searchHackerNews, createGithubIssue, PR review helpers, code sandbox - chat-client-test orchestrator for concurrent-send stress - references/hello-world chatAgent + triggerAndSubscribe examples CLI MCP tooling for chat.agent: - mcp/tools/agentChat.ts (start_agent_chat, send_agent_message, close_agent_chat) - mcp/tools/agents.ts + tasks.ts (list agents, agent run details) - dev-run-controller OOM kill + taskRunProcessPool tweaks - dev/managed entry-point hooks for skills bundling - buildWorker + bundleSkills (agent skills support) Includes ai-tool-helpers + mcp-agent-chat-sessions changesets, plus the streamdown@2 patch and pnpm-lock reconciliation. (Will be renamed to feature/ai-chat-reference-and-cli before push.)
The MCP `send_agent_message` was building wire payloads with a legacy `messages: [...]` array, but the agent's run loop now destructures `payload.message` (singular) per the slim-wire contract. Result: turn 1 entered `ai.streamText` with an empty messages array and threw `AI_InvalidPromptError: messages must not be empty`. Migrates all three send paths (session.in append, triggerTask fallback, and `trigger:upgrade-required` continuation) plus the `trigger: "close"` chunk to the slim-wire shape, and aligns the local `ChatInputChunk` type with the canonical one in ai-shared.ts.
…ction The hydrate-mode task uses `hydrateMessages` to read the full message chain from Postgres on every turn, so any `chat.history.*` mutation inside `onAction` (undo / rollback / remove / replace) was lost the moment the next turn started — the in-memory accumulator was overwritten by a fresh DB read. Write the mutated chain through with `prisma.chat.update` at the end of `onAction` so next turn's hydrate sees the pruned/edited state.
5c937df to
f5e3067
Compare
cf2fa07 to
fef74f4
Compare
| @@ -0,0 +1 @@ | |||
| {"sessionId":"a0e063d3-034b-40fe-90d0-7a6aff597e26","pid":72012,"procStart":"Tue May 12 17:34:30 2026","acquiredAt":1778608778207} No newline at end of file | |||
There was a problem hiding this comment.
🚩 Committed lock file with active session data
.claude/scheduled_tasks.lock contains a live session ID, PID, and timestamp from a specific development machine. This file appears to be machine-local state that shouldn't be committed to the repository. It could cause conflicts for other developers and doesn't serve a purpose in version control.
Was this helpful? React with 👍 or 👎 to provide feedback.
Summary
A complete Next.js reference project that exercises
chat.agentend-to-end, plus the CLI MCP tools that let Claude Code, Cursor, and similar IDE agents drive a deployedchat.agenttask from the editor. Builds on #3545.Design
references/ai-chatis a full Next.js app: prisma-backed persistence, multi-chat sidebar, per-chat model picker, debug panel, tool examples (getCurrentTime,searchHackerNews,createGithubIssue, PR review helpers, code sandbox), and smoke tests. It's intended both as a copy-paste starting point and as a place to regression-test SDK changes.The CLI gains MCP tools (
start_agent_chat,send_agent_message,close_agent_chat,list_agents) so an IDE agent can converse with a deployedchat.agenttask. The dev runtime adds one-shot OOM kill on the run controller and skills bundling in the build pipeline.Test plan
cd references/ai-chat && pnpm install && pnpm trigger:devstart_agent_chatagainst the running dev task, send a message, verify the response streams backStack
Part of a 4-PR stack. Merge bottom-up.
main— Sessions dashboard + chat-ready hardeningchat.agentruntime + browser transportThis is part 2 of 5 in a stack made with GitButler: